﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing.Design
Imports System.Reflection
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler
Imports System.Workflow.ComponentModel.Design
Imports System.Workflow.Runtime

Namespace System.Workflow.Activities
    <ActivityValidator(GetType(HandleExternalEventActivityValidator)), SRCategory("Base"), SRDescription("HandleExternalEventActivityDescription"), DefaultEvent("Invoked"), Designer(GetType(HandleExternalEventActivityDesigner), GetType(IDesigner))> _
    Public Class HandleExternalEventActivity
        Inherits Activity
        Implements IEventActivity, IPropertyValueProvider, IActivityEventListener([Of] QueueEventArgs), IDynamicPropertyTypeProvider
        ' Events
        <SRDescription("OnAfterMethodInvokeDescr"), SRCategory("Handlers"), MergableProperty(False)> _
        Public Custom Event Invoked As EventHandler([Of] ExternalDataEventArgs)
            AddHandler(ByVal value As EventHandler([Of] ExternalDataEventArgs))
                MyBase.AddHandler(HandleExternalEventActivity.InvokedEvent, value)
            End AddHandler
            RemoveHandler(ByVal value As EventHandler([Of] ExternalDataEventArgs))
                MyBase.RemoveHandler(HandleExternalEventActivity.InvokedEvent, value)
            End RemoveHandler
        End Event

        ' Methods
        Public Sub New()
            MyBase.SetReadOnlyPropertyValue(HandleExternalEventActivity.ParameterBindingsProperty, New WorkflowParameterBindingCollection(Me))
        End Sub

        Public Sub New(ByVal name As String)
            MyBase.New(name)
            MyBase.SetReadOnlyPropertyValue(HandleExternalEventActivity.ParameterBindingsProperty, New WorkflowParameterBindingCollection(Me))
        End Sub

        Protected NotOverridable Overrides Function Cancel(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Me.UnsubscribeForActivity(executionContext)
            Return ActivityExecutionStatus.Closed
        End Function

        Protected NotOverridable Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Dim args As Object() = Nothing
            Dim status As ActivityExecutionStatus = InboundActivityHelper.ExecuteForActivity(Me, executionContext, Me.InterfaceType, Me.EventName, args)
            If (status = ActivityExecutionStatus.Closed) Then
                Me.RaiseEvent(args)
                Me.UnsubscribeForActivity(executionContext)
                executionContext.CloseActivity
                Return status
            End If
            If Not Me.ActivitySubscribed Then
                Me.ActivitySubscribed = CorrelationService.Subscribe(executionContext, Me, Me.InterfaceType, Me.EventName, Me, MyBase.WorkflowInstanceId)
            End If
            Return ActivityExecutionStatus.Executing
        End Function

        Friend Sub GetParameterPropertyDescriptors(ByVal properties As IDictionary)
            If (Not Me.Site Is Nothing) Then
                If (DirectCast(Me.Site.GetService(GetType(ITypeProvider)), ITypeProvider) Is Nothing) Then
                    Throw New InvalidOperationException(SR.GetString("General_MissingService", New Object() { GetType(ITypeProvider).FullName }))
                End If
                Dim interfaceType As Type = Me.InterfaceType
                If ((Not interfaceType Is Nothing) AndAlso (MyBase.GetType Is GetType(HandleExternalEventActivity))) Then
                    Dim eventInfo As EventInfo = interfaceType.GetEvent(Me.EventName)
                    If (Not eventInfo Is Nothing) Then
                        Dim eventHandlerType As Type = TypeProvider.GetEventHandlerType(eventInfo)
                        If (Not eventHandlerType Is Nothing) Then
                            Dim method As MethodInfo = eventHandlerType.GetMethod("Invoke")
                            Dim list As New ArrayList
                            If (Not method Is Nothing) Then
                                list.AddRange(method.GetParameters)
                                If (Not method.ReturnType Is GetType(Void)) Then
                                    list.Add(method.ReturnParameter)
                                End If
                            End If
                            Dim info3 As ParameterInfo
                            For Each info3 In list
                                Dim descriptor As PropertyDescriptor = New ParameterInfoBasedPropertyDescriptor(GetType(HandleExternalEventActivity), info3, True, New Attribute() { DesignOnlyAttribute.Yes })
                                properties.Item(descriptor.Name) = descriptor
                            Next
                        End If
                    End If
                End If
            End If
        End Sub

        Protected NotOverridable Overrides Function HandleFault(ByVal executionContext As ActivityExecutionContext, ByVal exception As Exception) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (exception Is Nothing) Then
                Throw New ArgumentNullException("exception")
            End If
            Dim status As ActivityExecutionStatus = Me.Cancel(executionContext)
            If (status = ActivityExecutionStatus.Canceling) Then
                Return ActivityExecutionStatus.Faulting
            End If
            Return status
        End Function

        Protected NotOverridable Overrides Sub Initialize(ByVal provider As IServiceProvider)
            If (provider Is Nothing) Then
                Throw New ArgumentNullException("provider")
            End If
            If ((Not MyBase.IsDynamicActivity AndAlso Not Me.IsNestedUnderMultiInstanceContainer) OrElse Me.IsInitializingUnderMultiInstanceContainer) Then
                Dim interfaceType As Type = Me.InterfaceType
                Dim eventName As String = Me.EventName
                Dim comparable As IComparable = Nothing
                If CorrelationResolver.IsInitializingMember(interfaceType, eventName, Nothing) Then
                    comparable = New EventQueueName(interfaceType, eventName)
                End If
                MyBase.SetValue(HandleExternalEventActivity.QueueNameProperty, comparable)
                CorrelationService.Initialize(provider, Me, interfaceType, eventName, MyBase.WorkflowInstanceId)
            End If
        End Sub

        Protected Overrides Sub InitializeProperties()
            ActivityHelpers.InitializeCorrelationTokenCollection(Me, Me.CorrelationToken)
            Dim interfaceType As Type = Me.InterfaceType
            If (interfaceType Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("InterfaceTypeMissing", New Object() { MyBase.Name }))
            End If
            Dim eventName As String = Me.EventName
            If (eventName Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("EventNameMissing", New Object() { MyBase.Name }))
            End If
            Dim info As EventInfo = interfaceType.GetEvent(eventName)
            If (info Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("MethodInfoMissing", New Object() { Me.EventName, Me.InterfaceType.Name }))
            End If
            InvokeHelper.InitializeParameters(info.EventHandlerType.GetMethod("Invoke"), Me.ParameterBindings)
            MyBase.InitializeProperties
        End Sub

        Protected Overrides Sub OnClosed(ByVal provider As IServiceProvider)
            MyBase.RemoveProperty(HandleExternalEventActivity.ActivitySubscribedProperty)
        End Sub

        Protected Overridable Sub OnInvoked(ByVal e As EventArgs)
        End Sub

        Private Sub [RaiseEvent](ByVal args As Object())
            If (Not args Is Nothing) Then
                Dim e As ExternalDataEventArgs = [TryCast](args(1),ExternalDataEventArgs)
                Me.OnInvoked(e)
                MyBase.RaiseGenericEvent([Of] ExternalDataEventArgs)(HandleExternalEventActivity.InvokedEvent, args(0), e)
            Else
                Me.OnInvoked(EventArgs.Empty)
                MyBase.RaiseGenericEvent([Of] EventArgs)(HandleExternalEventActivity.InvokedEvent, Me, EventArgs.Empty)
            End If
        End Sub

        Private Sub System.Workflow.Activities.IEventActivity.Subscribe(ByVal parentContext As ActivityExecutionContext, ByVal parentEventHandler As IActivityEventListener([Of] QueueEventArgs)) Implements IEventActivity.Subscribe
            If (parentContext Is Nothing) Then
                Throw New ArgumentNullException("parentContext")
            End If
            If (parentEventHandler Is Nothing) Then
                Throw New ArgumentNullException("parentEventHandler")
            End If
            CorrelationService.Subscribe(parentContext, Me, Me.InterfaceType, Me.EventName, parentEventHandler, MyBase.WorkflowInstanceId)
        End Sub

        Private Sub System.Workflow.Activities.IEventActivity.Unsubscribe(ByVal parentContext As ActivityExecutionContext, ByVal parentEventHandler As IActivityEventListener([Of] QueueEventArgs)) Implements IEventActivity.Unsubscribe
            If (parentContext Is Nothing) Then
                Throw New ArgumentNullException("parentContext")
            End If
            If (parentEventHandler Is Nothing) Then
                Throw New ArgumentNullException("parentEventHandler")
            End If
            CorrelationService.Unsubscribe(parentContext, Me, Me.InterfaceType, Me.EventName, parentEventHandler)
        End Sub

        Private Function System.Workflow.ComponentModel.Design.IPropertyValueProvider.GetPropertyValues(ByVal context As ITypeDescriptorContext) As ICollection Implements IPropertyValueProvider.GetPropertyValues
            Dim strings As New StringCollection
            If (Not Me.InterfaceType Is Nothing) Then
                If Not (context.PropertyDescriptor.Name = "EventName") Then
                    Return strings
                End If
                Dim info As EventInfo
                For Each info In Me.InterfaceType.GetEvents((BindingFlags.Public Or (BindingFlags.Instance Or BindingFlags.DeclaredOnly)))
                    strings.Add(info.Name)
                Next
            End If
            Return strings
        End Function

        Private Sub System.Workflow.ComponentModel.IActivityEventListener<System.Workflow.ComponentModel.QueueEventArgs>.OnEvent(ByVal sender As Object, ByVal e As QueueEventArgs) Implements IActivityEventListener([Of] QueueEventArgs).OnEvent
            Dim context As ActivityExecutionContext = DirectCast(sender, ActivityExecutionContext)
            Dim activity As HandleExternalEventActivity = [TryCast](context.Activity,HandleExternalEventActivity)
            If (activity.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                Dim args As Object() = Nothing
                If (InboundActivityHelper.ExecuteForActivity(Me, context, Me.InterfaceType, Me.EventName, args) = ActivityExecutionStatus.Closed) Then
                    Me.RaiseEvent(args)
                    Me.UnsubscribeForActivity(context)
                    context.CloseActivity
                End If
            End If
        End Sub

        Private Function System.Workflow.ComponentModel.IDynamicPropertyTypeProvider.GetAccessType(ByVal serviceProvider As IServiceProvider, ByVal propertyName As String) As AccessTypes Implements IDynamicPropertyTypeProvider.GetAccessType
            If (propertyName Is Nothing) Then
                Throw New ArgumentNullException("propertyName")
            End If
            Return AccessTypes.Read
        End Function

        Private Function System.Workflow.ComponentModel.IDynamicPropertyTypeProvider.GetPropertyType(ByVal serviceProvider As IServiceProvider, ByVal propertyName As String) As Type Implements IDynamicPropertyTypeProvider.GetPropertyType
            If (propertyName Is Nothing) Then
                Throw New ArgumentNullException("propertyName")
            End If
            Dim properties As New Dictionary([Of] String, Object)
            Me.GetParameterPropertyDescriptors(properties)
            If properties.ContainsKey(propertyName) Then
                Dim descriptor As ParameterInfoBasedPropertyDescriptor = [TryCast](properties.Item(propertyName),ParameterInfoBasedPropertyDescriptor)
                If (Not descriptor Is Nothing) Then
                    Return descriptor.ParameterType
                End If
            End If
            Return Nothing
        End Function

        Private Sub UnsubscribeForActivity(ByVal context As ActivityExecutionContext)
            If Me.ActivitySubscribed Then
                CorrelationService.Unsubscribe(context, Me, Me.InterfaceType, Me.EventName, Me)
                Me.ActivitySubscribed = False
            End If
        End Sub


        ' Properties
        Private Property ActivitySubscribed As Boolean
            Get
                Return CBool(MyBase.GetValue(HandleExternalEventActivity.ActivitySubscribedProperty))
            End Get
            Set(ByVal value As Boolean)
                MyBase.SetValue(HandleExternalEventActivity.ActivitySubscribedProperty, value)
            End Set
        End Property

        <MergableProperty(False), SRCategory("Activity"), RefreshProperties(RefreshProperties.All), SRDescription("CorrelationSetDescr"), TypeConverter(GetType(CorrelationTokenTypeConverter)), DefaultValue(CStr(Nothing))> _
        Public Overridable Property CorrelationToken As CorrelationToken
            Get
                Return [TryCast](MyBase.GetValue(HandleExternalEventActivity.CorrelationTokenProperty),CorrelationToken)
            End Get
            Set(ByVal value As CorrelationToken)
                MyBase.SetValue(HandleExternalEventActivity.CorrelationTokenProperty, value)
            End Set
        End Property

        <MergableProperty(False), RefreshProperties(RefreshProperties.All), DefaultValue(""), TypeConverter(GetType(PropertyValueProviderTypeConverter)), SRCategory("Activity"), SRDescription("ExternalEventNameDescr")> _
        Public Overridable Property EventName As String
            Get
                Return [TryCast](MyBase.GetValue(HandleExternalEventActivity.EventNameProperty),String)
            End Get
            Set(ByVal value As String)
                MyBase.SetValue(HandleExternalEventActivity.EventNameProperty, value)
            End Set
        End Property

        <DefaultValue(CStr(Nothing)), Editor(GetType(TypeBrowserEditor), GetType(UITypeEditor)), SRCategory("Activity"), RefreshProperties(RefreshProperties.All), SRDescription("HelperExternalDataExchangeDesc"), TypeFilterProvider(GetType(ExternalDataExchangeInterfaceTypeFilterProvider))> _
        Public Overridable Property InterfaceType As Type
            Get
                Return [TryCast](MyBase.GetValue(HandleExternalEventActivity.InterfaceTypeProperty),Type)
            End Get
            Set(ByVal value As Type)
                MyBase.SetValue(HandleExternalEventActivity.InterfaceTypeProperty, value)
            End Set
        End Property

        Private ReadOnly Property IsInitializingUnderMultiInstanceContainer As Boolean
            Get
                Dim parent As CompositeActivity = MyBase.Parent
                Dim activity2 As Activity = Me
                Do While (Not parent Is Nothing)
                    If TypeOf parent Is ReplicatorActivity Then
                        Exit Do
                    End If
                    If Not parent.GetActivityByName(activity2.QualifiedName).Equals(activity2) Then
                        Return False
                    End If
                    activity2 = parent
                    parent = parent.Parent
                Loop
                Return ((Not parent Is Nothing) AndAlso Not parent.GetActivityByName(activity2.QualifiedName).Equals(activity2))
            End Get
        End Property

        Private ReadOnly Property IsNestedUnderMultiInstanceContainer As Boolean
            Get
                Dim activity As CompositeActivity = MyBase.Parent
                Do While (Not activity Is Nothing)
                    If TypeOf activity Is ReplicatorActivity Then
                        Return True
                    End If
                    activity = activity.Parent
                Loop
                Return False
            End Get
        End Property

        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
        Public ReadOnly Property ParameterBindings As WorkflowParameterBindingCollection
            Get
                Return [TryCast](MyBase.GetValue(HandleExternalEventActivity.ParameterBindingsProperty),WorkflowParameterBindingCollection)
            End Get
        End Property

        <SRCategory("Activity"), DefaultValue(CStr(Nothing)), SRDescription("RoleDescr"), Editor(GetType(BindUITypeEditor), GetType(UITypeEditor))> _
        Public Property Roles As WorkflowRoleCollection
            Get
                Return [TryCast](MyBase.GetValue(HandleExternalEventActivity.RolesProperty),WorkflowRoleCollection)
            End Get
            Set(ByVal value As WorkflowRoleCollection)
                MyBase.SetValue(HandleExternalEventActivity.RolesProperty, value)
            End Set
        End Property

        Private ReadOnly Property System.Workflow.Activities.IEventActivity.QueueName As IComparable
            Get
                Dim comparable As IComparable = DirectCast(MyBase.GetValue(HandleExternalEventActivity.QueueNameProperty), IComparable)
                If (Not comparable Is Nothing) Then
                    Return comparable
                End If
                Return CorrelationService.ResolveQueueName(Me, Me.InterfaceType, Me.EventName)
            End Get
        End Property


        ' Fields
        Private Shared ActivitySubscribedProperty As DependencyProperty = DependencyProperty.Register("ActivitySubscribed", GetType(Boolean), GetType(HandleExternalEventActivity), New PropertyMetadata(False))
        Public Shared ReadOnly CorrelationTokenProperty As DependencyProperty = DependencyProperty.Register("CorrelationToken", GetType(CorrelationToken), GetType(HandleExternalEventActivity), New PropertyMetadata(DependencyPropertyOptions.Metadata))
        Public Shared ReadOnly EventNameProperty As DependencyProperty = DependencyProperty.Register("EventName", GetType(String), GetType(HandleExternalEventActivity), New PropertyMetadata("", DependencyPropertyOptions.Metadata, New Attribute() { New ValidationOptionAttribute(ValidationOption.Required) }))
        Public Shared ReadOnly InterfaceTypeProperty As DependencyProperty = DependencyProperty.Register("InterfaceType", GetType(Type), GetType(HandleExternalEventActivity), New PropertyMetadata(Nothing, DependencyPropertyOptions.Metadata, New Attribute() { New ValidationOptionAttribute(ValidationOption.Required) }))
        Public Shared ReadOnly InvokedEvent As DependencyProperty = DependencyProperty.Register("Invoked", GetType(EventHandler([Of] ExternalDataEventArgs)), GetType(HandleExternalEventActivity))
        Public Shared ReadOnly ParameterBindingsProperty As DependencyProperty = DependencyProperty.Register("ParameterBindings", GetType(WorkflowParameterBindingCollection), GetType(HandleExternalEventActivity), New PropertyMetadata((DependencyPropertyOptions.Metadata Or DependencyPropertyOptions.ReadOnly), New Attribute() { New BrowsableAttribute(False), New DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content) }))
        Private Shared QueueNameProperty As DependencyProperty = DependencyProperty.Register("QueueName", GetType(IComparable), GetType(HandleExternalEventActivity))
        Friend Shared ReadOnly ReservedParameterNames As ArrayList = New ArrayList(New String() { "Name", "Enabled", "Description", "EventName", "InterfaceType", "Invoked", "Roles" })
        Public Shared ReadOnly RolesProperty As DependencyProperty = DependencyProperty.Register("Roles", GetType(WorkflowRoleCollection), GetType(HandleExternalEventActivity))
    End Class
End Namespace

